【小ネタ】direnv + assume-roleでクレデンシャルを取得、有効期限を設定してみる
こんにちは(U・ω・U)
AWS事業部の深澤です。
皆さん、AWSのクレデンシャルを取得される際、どのような方法を取られていますか。色々な方法がありますが、例えば環境変数から取得する方法がまず考えられますよね。
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-envvars.html
例えば以下のような変数でクレデンシャル情報を取得できます。
サポートされている環境変数 AWS CLI は次の環境変数をサポートしています。
・ AWS_ACCESS_KEY_ID – IAM ユーザーまたはロールに関連付けられる AWS アクセスキーを指定します。
・ AWS_SECRET_ACCESS_KEY – アクセスキーに関連付けられるシークレットキーを指定します。これは、基本的にアクセスキーの「パスワード」です。
ただ、色んな案件や環境(AWSアカウント)があった場合にはこれは煩雑です。
他には以下を参考にしながら、事前に名前付きプロファイルを設定しておき、
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-profiles.html
SHELL変数を用いて次のように個別のAWSアカウントを呼び出すこともできます。~/.aws/credentialsに以下のように記載し、
[user1] aws_access_key_id=AKIAXXXXXXXXXXXXXXXX aws_secret_access_key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
次のように実行します。
AWS_PROFILE=user1 aws s3 ls
しかし、この方法もいちいちSHELL変数込みでコマンドを打つのは煩雑ですよね。
ここにdirenvと呼ばれるものがあります。
https://github.com/direnv/direnv
これはディレクトリを遷移すると環境変数を設定してくれるというものです。
早速インストールしてみましょう。
direnvのセットアップ
Macの場合はbrewで提供されています。便利ですね。
$ brew install direnv
その後、shellにフックを設置してやる必要があります。
https://github.com/direnv/direnv/blob/master/docs/hook.md
自分はzshを使用しているので、~/.zshrcに次のように書き込みました。
eval "$(direnv hook zsh)"
次に環境変数を設定したいディレクトリに遷移して以下のコマンドを実行し、環境変数を設定します。
$ cd user1 $ direnv edit .
以下のコマンドを書き込みます。
export AWS_PROFILE=user1
こんな感じのメッセージが出れば設定完了です。
direnv: loading .envrc direnv: export +AWS_PROFILE
ちなみにdirenv editでファイルを開く場合は、$EDITOR環境変数に好みのエディタを設定しておく必要があります。
https://github.com/direnv/direnv/blob/master/man/direnv.1.md
Note that direnv edit . is a handy shortcut that open the file in your $EDITOR and automatically allows it if the file's modification time has changed.
僕はzshとvimを使っているので次のようになります。
~/.zshrcに以下を記載します。
EDITOR=vim
これでディレクトリ遷移にてAWSのクレデンシャルが取得できるようになりました。しかし、ローカルに各環境のアクセスキーやシークレットキーを入れるのはあまり良くありません。さてここからが本題です。
スイッチロール
AWSにはスイッチロールという機能があります。
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_use_switch-role-console.html
AWSアカウントを踏み台にして、他のAWS環境に一時的な認証を行いログインする仕組みです。つまりこれを用いることで1つのAWSアカウントのみアクセスキーとシークレットキーを記載するだけで他の環境に入れるので、余計なキーを手元のconfigに記載せずに済みます。本記事では具体的にこのスイッチロールをどのように設定するかについては設定を割愛させていただきますが、参考までに遷移先のロールに付けた信頼関係ポリシーを貼らせていただきます。sample_switch_roleという名前でロールを作成しました。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::遷移元アカウントの番号:user/遷移元アカウントのIAMユーザ名" }, "Action": "sts:AssumeRole", "Condition": { "Bool": { "aws:MultiFactorAuthPresent": "true" } } } ] }
assume-role
assume-roleで有名なのは以下の2つかと思います。
https://github.com/remind101/assume-role
https://github.com/coinbase/assume-role
僕がこれまで使ってきたのはremind101/assume-roleでしたが、※こちらはセッションの継続時間を指定することができません。そこで今回はcoinbase/assume-roleをセットアップし、セッションの時間を検証してみようと思います。
※ こちらですがremind101/assume-roleでも以下のようにコマンドを用いればセッション継続時間を指定できると指摘をいただきました。
$ assume-role -duration 2h
こちらは改めて検証し後日修正させていただきますm(_ _)m
2019年10月9日追記
検証した結果、「remind101/assume-roleではセッションの継続時間を指定することができない」は誤りであることが後の検証で分かりました。詳細は以下のブログをご覧ください。
【小ネタ】remind101/assume-roleのduration機能について 〜assume-roleツールの比較もあるよ〜
coinbase/assume-roleのセットアップ
インストール方法に関しては使用されているOS別に公式ドキュメントを参照して下さい。
https://github.com/coinbase/assume-role#installation
自分はMacを使用していますので、brewで簡単にインストールできました。
$ brew tap coinbase/assume-role $ brew install assume-role
これでassume-roleコマンドが使えるようになりました。実際に使うには毎回事前にスクリプトを呼び出す必要があります。
source $(which assume-role)
続いて.aws/credentialsへ遷移元アカウントのクレデンシャル情報を書き込みます。僕は次のように書き込みました。これ以降のコマンド例等は以下の情報を前提とします。
[bastion] aws_access_key_id=AKIAXXXXXXXXXXXXXXXX aws_secret_access_key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
続いて.aws/configにconfig情報も書き込みます。
[profile bastion] region=ap-northeast-1
そしてこの遷移元(bastion)情報をAWS_PROFILE_ASSUME_ROLEという環境変数に反映します。
export AWS_PROFILE_ASSUME_ROLE=bastion
これも毎回叩くのは煩雑かと思いますので、zshなら~/.zshrcとかbashなら~/.bashrcに記載した方が良いかと思います。
最後に遷移先となるawsアカウント番号を.aws/accountsに記載しましょう。
{ "private_aws": "123456789012" }
これで準備完了です。
実際に一時的認証状を取得してみる
これまで準備してきた情報を次のようにコマンドに当てはめます。
assume-role .aws/accountsに記載したアカウント名 遷移先アカウントで用意したロール名
従って、本記事で準備してきた内容を当てはめると次のようになります。
$ assume-role private_aws sample_switch_role MFA Token:
上記のようにMFA Tokenを聞かれるので入力しましょう。AWS_ACCESS_KEY_ID等の認証系情報が環境変数に書き込まれているか or 何かしらのawsコマンドでawsのリソース状況を確認する等の方法で正しく認証できたかを確認できます。
env | grep AWS_ACCESS_KEY_ID AWS_ACCESS_KEY_ID=ASIAXXXXXXXXXXXXXXXX
有効期限の設定
有効期限はAWS_ROLE_SESSION_TIMEOUTという環境変数を設定することで設定ができます。
$ export AWS_ROLE_SESSION_TIMEOUT=43200
こちらに入力した秒数期間分、セッションが続きます。上記の例だと12時間続くことになりますね。実際に短く設定して検証しようとしたら下限は900秒(15分)でした。
$ assume-role private_aws sample_switch_role Using assume-role default profile: bastion MFA Token: 123456 Parameter validation failed: Invalid range for parameter DurationSeconds, value: 60, valid range: 900-inf Failed to export session envars.
実際に900に設定して待機した結果、セッションが切れることを確認できました。期限設定は正しく機能しているようです。
$ aws s3 ls 2019-09-19 11:42:45 XXXXXXXXXXXXXXXXXXXXXX-bucket 〜15分後〜 $ aws s3 ls An error occurred (ExpiredToken) when calling the ListBuckets operation: The provided token has expired.
direnvとの組み合わせ
コマンドの事前に設定が必要なコマンドをdirenvに入れれば良いです。対象のディレクトリ配下でdirenv edit .を実行。次のコマンドを入れましょう。
source $(which assume-role) export AWS_ROLE_SESSION_TIMEOUT=900 assume-role private_aws sample_switch_role
これでディレクトリに遷移するだけで次のようになります。
$ cd private_aws Using assume-role default profile: bastion MFA Token: 123456 Success! IAM session envars are exported. direnv: export +AWS_ACCESS_KEY_ID +AWS_ACCOUNT_ID +AWS_ACCOUNT_NAME +AWS_ACCOUNT_ROLE +AWS_ROLE_SESSION_TIMEOUT +AWS_SECRET_ACCESS_KEY +AWS_SECURITY_TOKEN +AWS_SESSION_ACCESS_KEY_ID +AWS_SESSION_SECRET_ACCESS_KEY +AWS_SESSION_SECURITY_TOKEN +AWS_SESSION_SESSION_TOKEN +AWS_SESSION_TOKEN +GEO_ENV +ROLE_SESSION_START ~AWS_SESSION_START
注意
ロールが連鎖している場合にはセッションは1時間しか続きません。 https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_terms-and-concepts.html
ロールの連鎖では、AWS CLI または API ロールセッションは最長 1 時間に制限されます。AssumeRole API オペレーションを使用してロールを引き受ける場合は、DurationSeconds パラメータを使用してロールセッションの期間を指定できます。パラメータの値は、ロールの最大セッション期間設定によって、最大 43200 秒 (12 時間) まで指定できます。ただし、ロールの連鎖を使用してロールを引き受ける場合、DurationSeconds パラメータ値で 1 時間を超える値を指定すると、オペレーションは失敗します。
感想
今回は自分のPCの設定がてらcoinbase/assume-roleを検証してみました。よくterraformで環境に設定を適用中にセッションが切れて実際の環境とstateファイルに差分が発生して元に戻すのが大変ってことがあったので今回セッションの時間調整ができてすごい便利だなと感じました!懸念としてはshellベースで動いているのでパッケージをyumとかbrewで更新した時に動作がおかしくなったりすることもあるのかなと思いましたね。
以上、深澤(@shun_quartet)でした!